package com.xiaomaoguai.fcp.pre.kepler.mock;

import com.xiaomaoguai.fcp.pre.kepler.common.config.listener.glue.GlueConfigurator;
import com.xiaomaoguai.fcp.pre.kepler.convert.core.ConvertTemplate;
import feign.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.MediaTypeExpression;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * @author August.Zhang
 * @version v1.0.0
 * @date 2020/2/14 14:11
 * @since JDK 1.8
 */
@Configuration
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class FeignRequestMappingConfiguration {

	@Autowired
	private RequestMappingHandlerMapping requestMappingHandlerMapping;

	@Autowired
	private AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor;

	@Autowired
	private ConfigurableListableBeanFactory beanFactory;

	@PostConstruct
	public void init() throws Exception {
		Set<Method> methodSet = FeignMethodHolder.getMethodSet();

		for (final Method method : methodSet) {
			Class<?> clazz = method.getDeclaringClass();
			String simpleName = clazz.getSimpleName();
			String methodName = method.getName();

			RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
			String[] paths = requestMapping.path();
			for (final String path : paths) {
				registrar(path, simpleName, methodName);
			}
		}

	}

	/**
	 * 请求URL
	 *
	 * 格转名称
	 *
	 * 格转参数
	 *
	 * 方法返回值
	 */
	public void registrar(String uri, String className, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
		MockHandler mockHandler = new MockHandler(className, methodName);
		mockHandler.setGlueConfigurator(beanFactory.getBean(GlueConfigurator.class));
		mockHandler.setConvertTemplate(beanFactory.getBean(ConvertTemplate.class));

		PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition(uri);
		RequestMethodsRequestCondition requestMethodsRequestCondition = new RequestMethodsRequestCondition();
		ParamsRequestCondition paramsRequestCondition = new ParamsRequestCondition();
		HeadersRequestCondition headersRequestCondition = new HeadersRequestCondition();
		ConsumesRequestCondition consumesRequestCondition = new ConsumesRequestCondition();
		ProducesRequestCondition producesRequestCondition = new ProducesRequestCondition();
		MediaTypeExpression mediaTypeExpression = new MediaTypeExpression() {

			@Override
			public MediaType getMediaType() {
				return MediaType.APPLICATION_JSON_UTF8;
			}

			@Override
			public boolean isNegated() {
				return false;
			}
		};
		producesRequestCondition.getExpressions().add(mediaTypeExpression);
		RequestMappingInfo requestMappingInfo = new RequestMappingInfo(uri, patternsRequestCondition, requestMethodsRequestCondition, paramsRequestCondition, headersRequestCondition, consumesRequestCondition, producesRequestCondition, null);
		DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
		autowiredAnnotationBeanPostProcessor.processInjection(mockHandler);

		String beanName = MockHandler.class.getName() + "_" + uri;
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MockHandler.class);
		defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());

		Class<? extends RequestMappingHandlerMapping> clazz = requestMappingHandlerMapping.getClass();
		Class<?> superclass = clazz.getSuperclass().getSuperclass();
		Method detectHandlerMethods = superclass.getDeclaredMethod("detectHandlerMethods", Object.class);
		detectHandlerMethods.setAccessible(true);
		detectHandlerMethods.invoke(requestMappingHandlerMapping, beanName);

		requestMappingHandlerMapping.registerMapping(requestMappingInfo, mockHandler, mockHandler.getClass().getMethod("invoke", String.class));
	}

	@Bean
	public Logger.Level feignLoggerLevel() {
		//这里记录所有，根据实际情况选择合适的日志level
		return Logger.Level.FULL;
	}

}
